package com.hn.shopadmin.conf;

import com.github.pagehelper.util.StringUtil;
import com.hn.kiss.modules.sys.shopdb.entity.*;
import com.hn.shopadmin.compoent.AdminUserDetails;
import com.hn.shopadmin.compoent.JwtAuthenticationTokenFilter;
import com.hn.shopadmin.compoent.RestAuthenticationEntryPoint;
import com.hn.shopadmin.compoent.RestfulAccessDeniedHandler;
import com.hn.shopadmin.dto.JursdictionVsRoleDTO;
import com.hn.kiss.modules.sys.shopdb.mapper.JurisdictionMapper;
import com.hn.kiss.modules.sys.shopdb.mapper.RoleJurisdictionMapper;
import com.hn.kiss.modules.sys.shopdb.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;


/**
 * SpringSecurity的配置
 * Created by macro on 2018/4/26.
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleJurisdictionMapper roleJurisdictionMapper;
    @Autowired
    private JurisdictionMapper jurisdictionMapper;


    //拦截配合
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf()// 由于使用的是JWT，我们这里不需要csrf
                .disable()
                .sessionManagement()// 基于token，所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.GET,
                        "/swagger-ui.html/**",
                        "/swagger-resources/**",
                        "/webjars/**",
                        "/v2/**",
                        "/t/hello"
                ).permitAll()// 允许对于网站静态资源的无授权访问
                .antMatchers(HttpMethod.OPTIONS).permitAll();//跨域请求会先进行一次options请求
        //拼接权限的拦截
        List<JursdictionVsRoleDTO> roleJursdictionDTOS = listRoleJursdiction();
        for (JursdictionVsRoleDTO roleJursdictionDTO : roleJursdictionDTOS) {
            String[] roles = roleJursdictionDTO.getRoles().toArray(new String[roleJursdictionDTO.getRoles().size()]);
            //指定url,指定角色可以访问
            httpSecurity.authorizeRequests().antMatchers(roleJursdictionDTO.getJursdictionUrl()).hasAnyRole(roles);
        }
        //其他的需要登录
//        httpSecurity.authorizeRequests().anyRequest().authenticated();
        //其他的不拦截
        httpSecurity.authorizeRequests().anyRequest().permitAll();
        // 禁用缓存
        httpSecurity.headers().cacheControl();
        // 添加JWT filter
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        //添加自定义未授权和未登录结果返回
        httpSecurity.exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler())
                .authenticationEntryPoint(restAuthenticationEntryPoint());
    }

    //认证授权
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }


    //密码加密
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    //这里相当于实现UserDetailsService的接口,在登陆的时候调用,接口参数username
    @Bean
    public UserDetailsService userDetailsService() {
        //这里是java8的写法,返回一个方法
        //就是相当于实现UserDetailsService接口
        //最终返回AdminUserDetails
        return username -> {
            //这里应该通过数据,根据用户名查询用户信息,校验用户名密码是否正确
            User user = getUserByUserName(username);
            if (user != null) {
                //admin用户授予ADMIN的角色,添加角色需时要添加'ROLE_'前缀,因为源码判断会先加前缀在判断
                if (StringUtil.isNotEmpty(user.getRole())) {
                    user.setRole("ROLE_" + user.getRole());
                }
                return new AdminUserDetails(user);
            }
            throw new UsernameNotFoundException("用户名有误");
        };
    }

    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter();
    }
    @Bean
    public RestfulAccessDeniedHandler restfulAccessDeniedHandler() {
        return new RestfulAccessDeniedHandler();
    }
    @Bean
    public RestAuthenticationEntryPoint restAuthenticationEntryPoint() {
        return new RestAuthenticationEntryPoint();
    }

    /**
     * 允许跨域调用的过滤器
     */
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("*");
        config.setAllowCredentials(true);
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return new CorsFilter(source);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    /**
     *通过账号获取用户信息
     * @param username 手机号/邮箱/身份证号
     * @return
     */
    public User getUserByUserName(String username) {
        UserExample userExample = new UserExample();
        userExample.createCriteria().andEmallEqualTo(username);
        userExample.or().andPhoneEqualTo(username);
        userExample.or().andIdnoEqualTo(username);

        List<User> users = userMapper.selectByExample(userExample);
        if (users != null && users.size() > 0) {
            return users.get(0);
        }
        return null;
    }

    /**
     * 获取权限角色关联信息
     * @return
     */
    public List<JursdictionVsRoleDTO> listRoleJursdiction() {
        //角色权限关联列表
        ArrayList<JursdictionVsRoleDTO> roleJursdictionDTOS = new ArrayList<JursdictionVsRoleDTO>();

        //查询所有权限
        List<Jurisdiction> jurisdictions = jurisdictionMapper.selectByExample(new JurisdictionExample());
        jurisdictions.forEach(jurisdiction -> {
            //查询拥有该权限的角色列表
            RoleJurisdictionExample roleJurisdictionExample = new RoleJurisdictionExample();
            roleJurisdictionExample.createCriteria().andJurisdictionIdEqualTo(jurisdiction.getId());
            List<RoleJurisdiction> roleJurisdictions = roleJurisdictionMapper.selectByExample(roleJurisdictionExample);

            //对应权限权限列表不为空,拼接权限url和角色对应关系
            if (roleJurisdictions != null && roleJurisdictions.size() > 0) {
                //获取角色code列表
                List<String> roles = roleJurisdictions.stream()
                        .map(roleJurisdiction -> roleJurisdiction.getRoleCode())
                        .collect(Collectors.toList());

                //拼接
                JursdictionVsRoleDTO roleJursdictionDTO = new JursdictionVsRoleDTO();
                roleJursdictionDTO.setJursdictionUrl(jurisdiction.getUrl());
                roleJursdictionDTO.setRoles(roles);
                roleJursdictionDTOS.add(roleJursdictionDTO);
            }
        });
        return roleJursdictionDTOS;
    }

}
